/*
 * Copyright (c) 2013   Tobias Welther
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package de.tudarmstadt.kom.androidsensorapi.sensormanagement;

import android.hardware.Sensor;

import java.util.HashSet;
import java.util.Set;

import de.tudarmstadt.kom.androidsensorapi.sensor.data.AbstractSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.AccelerometerSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.BrightnessSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.GravitySensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.GyroscopeSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.LocationSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.MagneticFieldSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.OrientationSensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.data.concrete.ProximitySensorData;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.AbstractSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.acceleration.AccelerometerSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.brightness.BrightnessSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.gravity.GravitySensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.gyroscope.GyroscopeSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.location.LocationSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.magneticfield.MagneticFieldSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.orientation.OrientationSensor;
import de.tudarmstadt.kom.androidsensorapi.sensor.listener.proximity.ProximitySensor;

/**
 * Created by Tobias Welther
 */
public class SensorTypeToData {

    //TODO write some doc to every static

    public static final SensorTypeToData Accelerometer = new SensorTypeToData(AccelerometerSensorData.class, new AccelerometerSensor(), Sensor.TYPE_ACCELEROMETER);
    public static final SensorTypeToData Brightness = new SensorTypeToData(BrightnessSensorData.class, new BrightnessSensor(), Sensor.TYPE_LIGHT);
    public static final SensorTypeToData Gravity = new SensorTypeToData(GravitySensorData.class, new GravitySensor(), Sensor.TYPE_GRAVITY);
    public static final SensorTypeToData Gyroscope = new SensorTypeToData(GyroscopeSensorData.class, new GyroscopeSensor(), Sensor.TYPE_GYROSCOPE);
    public static final SensorTypeToData Location = new SensorTypeToData(LocationSensorData.class, new LocationSensor(), -1);
    public static final SensorTypeToData MagneticField = new SensorTypeToData(MagneticFieldSensorData.class, new MagneticFieldSensor(), Sensor.TYPE_MAGNETIC_FIELD);
    public static final SensorTypeToData Orientation = new SensorTypeToData(OrientationSensorData.class, new OrientationSensor(), Sensor.TYPE_ORIENTATION);
    public static final SensorTypeToData Proximity = new SensorTypeToData(ProximitySensorData.class, new ProximitySensor(), Sensor.TYPE_PROXIMITY);


    /**
     * This is a placeholder and representation to register / unregister ALL sensors.
     */
    public static final SensorTypeToData All = new SensorTypeToData(null, null, Sensor.TYPE_ALL);

    /**
     * This is a representative that no of the known Sensors is found.
     * It is actually just a Smart null.
     */
    public static final SensorTypeToData NONE = new SensorTypeToData(null, null, Sensor.TYPE_ORIENTATION);


    /**
     * This is the List of all Representations that can be made and are linked in the System.
     */
    public static final Set<SensorTypeToData> setOfAllSensors = fillSet(new HashSet<SensorTypeToData>());


    /**
     * Creates an initial Set with all Sensors and Classes corresponding.
     *
     * @param set to add to
     * @return the complete set.
     */
    private static Set<SensorTypeToData> fillSet(Set<SensorTypeToData> set){
        set.add(Accelerometer);
        set.add(Brightness);
        set.add(Gravity);
        set.add(Gyroscope);
        set.add(Location);
        set.add(MagneticField);
        set.add(Orientation);
        set.add(Proximity);

        return set;
    }


    /**
     * The Class the SensorData is associated to.
     */
    private final Class<? extends AbstractSensorData> sensorDataClass;

    /**
     * The AbstractSensor this is associated to.
     */
    private final AbstractSensor sensor;

    /**
     * The native Android Sensor Type associated.
     */
    private final int androidSensorType;


    /**
     * A new Association of all Relevant sensorInterface Data.
     *
     * WARNING: not all android SensorInterface Types are represented.
     * androidSensorType can be -1 if no android SensorInterface Type is found for Representation.
     *
     * @param sensorDataClass the Data class for the SensorInterface.
     * @param sensor the Sensor to register
     * @param androidSensorType the Android native type or -1 if not found.
     */
    private SensorTypeToData(Class<? extends AbstractSensorData> sensorDataClass,
                             AbstractSensor sensor,
                            int androidSensorType){

        this.androidSensorType = androidSensorType;
        this.sensor = sensor;
        this.sensorDataClass = sensorDataClass;
    }


    /**
     * The SensorInterface Data Class this SensorInterface Type is associated to
     */
    public Class<? extends AbstractSensorData> getSensorDataClass() {
        return sensorDataClass;
    }


    /**
     * The SensorInterface Class this SensorInterface Type is associated to
     *
     * This is protected to not pass the Sensor to the outside.
     */
    protected AbstractSensor getSensor() {
        return sensor;
    }


    /**
     * The Android native Sensor Types this SensorInterface Type is associated to.
     *
     * WARNING: can be empty if no SensorInterface is associated to. (iE. Location).
     */
    public int getAndroidSensorType() {
        return androidSensorType;
    }


    /**
     * Returns the Correct SensorTypeToData associated to a SensorDataClass.
     * Returns {@link #NONE} if no SensorData is found for that class.
     *
     * @return
     */
    public static SensorTypeToData getTypeByDataClass(Class<? extends  AbstractSensorData> dataClassToFind){
        for(SensorTypeToData type : setOfAllSensors){
            if(type.getSensorDataClass() == dataClassToFind){
                return type;
            }
        }

        return SensorTypeToData.NONE;
    }
}
